from odoo import api, fields, models, _
import io
import base64


class DocumentsShippingListWizard(models.TransientModel):
    _name = "documents.shipping_list.wizard"
    _description = "装运清单向导"

    language_mode = fields.Selection(
        selection=[("zh", "中文"), ("en", "英文"), ("it", "意大利文")],
        string="语言",
        default="zh",
        required=True,
    )
    export_format = fields.Selection(
        selection=[("xlsx", "XLSX")],
        string="导出格式",
        default="xlsx",
        required=True,
    )
    date = fields.Date(string="日期", default=fields.Date.context_today, required=True)
    company_id = fields.Many2one(
        comodel_name="res.company",
        string="公司",
        default=lambda self: self.env.company,
        required=True,
    )
    partner_id = fields.Many2one("res.partner", string="客户")

    package_ids = fields.Many2many(
        comodel_name="stock.quant.package",
        relation="documents_shipping_list_wizard_package_rel",
        column1="wizard_id",
        column2="package_id",
        string="包装",
        required=True,
    )
    note = fields.Text(string="备注")

    line_ids = fields.One2many(
        comodel_name="documents.shipping_list.wizard.line",
        inverse_name="wizard_id",
        string="明细行",
    )
    group_ids = fields.One2many(
        comodel_name="documents.shipping_list.wizard.group",
        inverse_name="wizard_id",
        string="分组",
    )
    total_volume = fields.Float(string="总体积")
    # 新增字段
    estimation_coefficient = fields.Float(string="估算系数", default=165.0, help="体积m³转换为重量kg的系数")
    total_weight = fields.Float(string="总重量(kg)", compute="_compute_total_weight", store=True)
    total_package_count = fields.Integer(string="包裹总数", compute="_compute_total_package_count", store=True)

    @api.depends('total_volume', 'estimation_coefficient')
    def _compute_total_weight(self):
        """计算总重量：体积(m³) × 估算系数"""
        for record in self:
            volume_m3 = record.total_volume / 1_000_000  # 将cm³转换为m³
            record.total_weight = volume_m3 * record.estimation_coefficient

    @api.depends('package_ids')
    def _compute_total_package_count(self):
        """计算包裹总数"""
        for record in self:
            record.total_package_count = len(record.package_ids)

    @api.onchange("package_ids")
    def _onchange_package_ids(self):
        """Auto-set the customer based on selected packages and build lines/groups.

        Tries to infer the customer by tracing package -> move lines -> picking/customer
        or sale order on the related move's sale line.
        """
        self.ensure_one()
        if not self.package_ids:
            self.partner_id = False
            self.line_ids = [(5, 0, 0)]
            self.group_ids = [(5, 0, 0)]
            self.total_volume = 0.0
            return

        StockMoveLine = self.env["stock.move.line"]
        move_lines = StockMoveLine.search([
            "|",
            ("result_package_id", "in", self.package_ids.ids),
            ("package_id", "in", self.package_ids.ids),
        ])

        # Infer a single partner for header convenience
        inferred_partner_ids = set()
        package_to_partner = {}
        for move_line in move_lines:
            picking_partner = move_line.picking_id.partner_id
            candidate_partner = picking_partner or move_line.move_id.sale_line_id.order_id.partner_id
            if candidate_partner:
                inferred_partner_ids.add(candidate_partner.id)
                # Map package to partner
                pkg = move_line.result_package_id or move_line.package_id
                if pkg:
                    package_to_partner[pkg.id] = candidate_partner.id
        if len(inferred_partner_ids) == 1:
            self.partner_id = list(inferred_partner_ids)[0]

        # Build detail lines per package
        new_lines = []
        total_volume = 0.0
        for package in self.package_ids:
            partner_id = package_to_partner.get(package.id) or package.owner_id.id or False
            ptype = package.package_type_id
            length = ptype.packaging_length if ptype else 0.0
            width = ptype.width if ptype else 0.0
            height = ptype.height if ptype else 0.0
            volume = (length or 0.0) * (width or 0.0) * (height or 0.0)
            total_volume += volume
            new_lines.append((0, 0, {
                "partner_id": partner_id,
                "package_id": package.id,
                "package_type_id": ptype.id if ptype else False,
                "length": length,
                "width": width,
                "height": height,
                "volume": volume,
            }))

        # Assign group numbers by distinct partner ordering by name/id
        # Create groups with package counts and group volume
        groups_by_partner = {}
        for line_vals in new_lines:
            vals = line_vals[2]
            partner_id = vals.get("partner_id") or 0
            groups_by_partner.setdefault(partner_id, {"package_count": 0, "volume": 0.0})
            groups_by_partner[partner_id]["package_count"] += 1
            groups_by_partner[partner_id]["volume"] += vals.get("volume") or 0.0

        # Sort partners by display name for consistent numbering
        ordered_partner_ids = [pid for pid in groups_by_partner.keys()]
        if ordered_partner_ids:
            partners = self.env["res.partner"].browse([pid for pid in ordered_partner_ids if pid])
            partners_by_id = {p.id: p for p in partners}
            ordered_partner_ids.sort(key=lambda pid: (partners_by_id.get(pid).display_name if pid in partners_by_id else "~"))
        number_by_partner = {pid: idx + 1 for idx, pid in enumerate(ordered_partner_ids)}

        # Write number into lines
        numbered_lines = []
        for line_vals in new_lines:
            vals = dict(line_vals[2])
            partner_id = vals.get("partner_id") or 0
            vals["number"] = number_by_partner.get(partner_id, 0)
            numbered_lines.append((0, 0, vals))

        # Build group records
        new_groups = []
        for partner_id, agg in groups_by_partner.items():
            new_groups.append((0, 0, {
                "partner_id": partner_id or False,
                "number": number_by_partner.get(partner_id, 0),
                "package_count": agg["package_count"],
                "volume": agg["volume"],
            }))

        self.line_ids = [(5, 0, 0)] + numbered_lines
        self.group_ids = [(5, 0, 0)] + new_groups
        self.total_volume = total_volume

    def action_export(self):
        self.ensure_one()
        # Build XLSX file in-memory
        try:
            import xlsxwriter  # type: ignore
        except Exception:  # pragma: no cover - fallback for environments without xlsxwriter
            return {"type": "ir.actions.act_window_close"}

        output = io.BytesIO()
        workbook = xlsxwriter.Workbook(output, {"in_memory": True})
        worksheet = workbook.add_worksheet("Shipping List")
        # 打印设置：A4 纵向，按页宽缩放，水平居中，边距
        try:
            worksheet.set_portrait()
            worksheet.set_paper(9)  # A4
            worksheet.fit_to_pages(1, 0)
            worksheet.center_horizontally()
            worksheet.set_margins(left=0.3, right=0.3, top=0.6, bottom=0.8)
        except Exception:
            pass

        # Formats
        fmt_header = workbook.add_format({"bold": True, "align": "center", "valign": "vcenter"})
        fmt_cell = workbook.add_format({})
        fmt_group = workbook.add_format({"bold": True})
        fmt_group_right = workbook.add_format({"bold": True, "align": "right"})
        fmt_group_right_6d = workbook.add_format({"bold": True, "align": "right", "num_format": "0.000000"})
        fmt_unit_left = workbook.add_format({"align": "left"})

        # Localized labels
        lang = self.language_mode or "zh"
        if lang == "zh":
            lbl_number = "编号"
            lbl_pkg_number = "包裹编号"
            lbl_len = "长度(cm)"
            lbl_w = "宽度(cm)"
            lbl_h = "高度(cm)"
            lbl_vol = "体积(cm³)"
            lbl_count = "数量"
            lbl_total = "合计:"
            lbl_total_vol = "总体积"
            lbl_total_weight = "总重量"
            lbl_group = "发运 {}"
        elif lang == "it":
            lbl_number = "Number"
            lbl_pkg_number = "Package Number"
            lbl_len = "Length(cm)"
            lbl_w = "Width(cm)"
            lbl_h = "Height(cm)"
            lbl_vol = "Volume(cm³)"
            lbl_count = "Count"
            lbl_total = "Totale:"
            lbl_total_vol = "Total"
            lbl_total_weight = "Total Weight"
            lbl_group = "Spedizione {}"
        else:  # en
            lbl_number = "Number"
            lbl_pkg_number = "Package Number"
            lbl_len = "Length(cm)"
            lbl_w = "Width(cm)"
            lbl_h = "Height(cm)"
            lbl_vol = "Volume(cm³)"
            lbl_count = "Count"
            lbl_total = "Total:"
            lbl_total_vol = "Total"
            lbl_total_weight = "Total Weight"
            lbl_group = "Shipment {}"

        # Headers - 增加数量列
        headers = [lbl_number, lbl_pkg_number, lbl_len, lbl_w, lbl_h, lbl_vol, lbl_count, lbl_total]
        for col, title in enumerate(headers):
            worksheet.write(0, col, title, fmt_header)

        # Column widths for readability - 调整列宽以适应新列
        worksheet.set_column(0, 0, 18)
        worksheet.set_column(1, 1, 22)
        worksheet.set_column(2, 5, 12)
        worksheet.set_column(6, 6, 8)  # 数量列
        worksheet.set_column(7, 7, 18)  # 合计列

        # Collect package -> location mapping using move lines (learned from controllers/main.py style)
        StockMoveLine = self.env["stock.move.line"].sudo()
        move_lines = StockMoveLine.search([
            "|",
            ("result_package_id", "in", self.package_ids.ids),
            ("package_id", "in", self.package_ids.ids),
        ])
        package_to_location = {}
        for ml in move_lines:
            pkg = ml.result_package_id or ml.package_id
            if not pkg:
                continue
            # 按要求以 location_id 分类
            if pkg.id not in package_to_location:
                package_to_location[pkg.id] = ml.location_id.id if ml.location_id else 0

        # Build rows per location group with package info and computed volume
        groups_map = {}
        for pkg in self.package_ids:
            loc_id = package_to_location.get(pkg.id, 0)
            ptype = pkg.package_type_id
            length = (ptype and ptype.packaging_length) or 0.0
            width = (ptype and ptype.width) or 0.0
            height = (ptype and ptype.height) or 0.0
            volume = (length or 0.0) * (width or 0.0) * (height or 0.0)
            entry = {
                "package": pkg,
                "length": length,
                "width": width,
                "height": height,
                "volume": volume,
            }
            data = groups_map.setdefault(loc_id, {"location": self.env["stock.location"].browse(loc_id) if loc_id else False, "rows": [], "volume": 0.0, "count": 0})
            data["rows"].append(entry)
            data["volume"] += volume
            data["count"] += 1  # 计数包裹

        # Order groups by location display name for stable numbering
        def _loc_key(item):
            loc = item[1]["location"]
            return (loc.display_name if loc else "~")

        ordered_groups = sorted(groups_map.items(), key=_loc_key)

        current_row = 1
        total_volume_cm3 = 0.0
        total_count = 0

        for idx, (_loc_id, data) in enumerate(ordered_groups, start=1):
            group_number = idx
            group_volume = data["volume"]
            group_count = data["count"]
            total_volume_cm3 += group_volume
            total_count += group_count

            # Group label in column A, count in column G, and group total in column H at the same row
            worksheet.write(current_row, 0, lbl_group.format(group_number), fmt_group)
            worksheet.write_number(current_row, 6, group_count, fmt_group)
            worksheet.write_number(current_row, 7, group_volume, fmt_group)
            worksheet.write(current_row, 8, "cm³", fmt_unit_left)

            # Lines of the group
            first_on_line = True
            for row in data["rows"]:
                pkg = row["package"]
                # first package stays on the same row as group title/total
                if not first_on_line:
                    current_row += 1
                worksheet.write(current_row, 1, pkg.name or "", fmt_cell)
                worksheet.write_number(current_row, 2, row["length"], fmt_cell)
                worksheet.write_number(current_row, 3, row["width"], fmt_cell)
                worksheet.write_number(current_row, 4, row["height"], fmt_cell)
                worksheet.write_number(current_row, 5, row["volume"], fmt_cell)
                first_on_line = False
            
            # 分组重量汇总（不显示m³），放在体积下面，重量与体积(cm³)同一列
            current_row += 1
            group_volume_m3 = group_volume / 1_000_000
            group_weight = group_volume_m3 * self.estimation_coefficient
            worksheet.write_number(current_row, 7, group_weight, fmt_group_right)
            worksheet.write(current_row, 8, "kg", fmt_unit_left)
            
            # move to next line and insert one blank line between groups
            current_row += 2

        # Final totals (包裹数量、体积cm³和m³、重量kg)
        worksheet.write(current_row, 5, lbl_total, fmt_group)
        worksheet.write_number(current_row, 6, total_count, fmt_group_right)
        worksheet.write_number(current_row, 7, total_volume_cm3, fmt_group_right)
        worksheet.write(current_row, 8, "cm³", fmt_unit_left)
        current_row += 1
        
        # 体积 m³
        volume_m3 = total_volume_cm3 / 1_000_000
        worksheet.write(current_row, 5, lbl_total_vol, fmt_group)
        worksheet.write_number(current_row, 7, volume_m3, fmt_group_right_6d)
        worksheet.write(current_row, 8, "m³", fmt_unit_left)
        current_row += 1
        
        # 重量 kg (使用估算系数)
        total_weight = volume_m3 * self.estimation_coefficient
        worksheet.write(current_row, 5, lbl_total_weight, fmt_group)
        worksheet.write_number(current_row, 7, total_weight, fmt_group_right)
        worksheet.write(current_row, 8, "kg", fmt_unit_left)

        workbook.close()
        output.seek(0)
        data = output.read()
        output.close()

        filename = f"shipping_list_{fields.Date.context_today(self)}.xlsx"
        attachment = self.env["ir.attachment"].create({
            "name": filename,
            "type": "binary",
            "datas": base64.b64encode(data),
            "res_model": self._name,
            "res_id": self.id,
            "mimetype": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        })

        return {
            "type": "ir.actions.act_url",
            "url": f"/web/content/{attachment.id}?download=true",
            "target": "self",
        }


class DocumentsShippingListWizardLine(models.TransientModel):
    _name = "documents.shipping_list.wizard.line"
    _description = "装运清单向导行"

    wizard_id = fields.Many2one(
        comodel_name="documents.shipping_list.wizard",
        required=True,
        ondelete="cascade",
    )
    number = fields.Integer(string="编号")
    partner_id = fields.Many2one("res.partner", string="客户")
    package_id = fields.Many2one("stock.quant.package", string="包装")
    package_type_id = fields.Many2one("stock.package.type", string="包装类型")
    length = fields.Float(string="长度")
    width = fields.Float(string="宽度")
    height = fields.Float(string="高度")
    volume = fields.Float(string="体积")


class DocumentsShippingListWizardGroup(models.TransientModel):
    _name = "documents.shipping_list.wizard.group"
    _description = "装运清单向导分组"

    wizard_id = fields.Many2one(
        comodel_name="documents.shipping_list.wizard",
        required=True,
        ondelete="cascade",
    )
    number = fields.Integer(string="编号")
    partner_id = fields.Many2one("res.partner", string="客户")
    package_count = fields.Integer(string="包装数量")
    volume = fields.Float(string="体积") 